大吉大利,今晚吃凤梨 —— pinia 迁移随记
pinia 相比 vuex 我觉得最大的提升有下面 3 个:
概念更简单
对 TypeScript 的支持更好
对 composition API 的支持更好
下面就分别举一个简单的例子来说明下。
概念更简单
pinia 里没有 mutation 的概念了,如果你要 mutate state,那就直接对它赋值;如果你的赋值有比较复杂的逻辑,那就把它包成一个 Action——对,Action 概念统一了,并且也没有所谓 dispatch 和 commit 的概念了。
换言之,不管做什么 mutate,如果是同步的,我们就把 Action 定义成普通的函数,如果是异步的,我们就把 Action 定义成 async function,这也算是所见即所得了。
与此同时,namespace 的概念也没有了,所有的 module 都是一个独立的 store 模块,也不需要一个注册到根模块上这一类的动作,完全可以独立工作。而在 App 入口初的 createPinia 和 app.use(pinia) 更多来说是注册一个 provider 一类的东西,并没有真正注册各个 module。
因为在 vuex 里,modules 的注册是自顶而下的,index module 需要收集所有的 sub modules 然后再一次性 createStore。而 pinia 的 modules 其实没有集中注册的概念,所以可以定义得如此松散。
不过目前我们还是保留了 modules 目录,主要是为了稍微好看一点 ……
对 TypeScript 支持更好
因为没有了 commit 和 dispatch,也没有了 namespace,原本用字符串做 ID 的恶心事情都没了,而是
getters -> 直接访问字段
dispatch -> 直接调用成员方法
commit -> 当作 action 一样处理,直接调用成员方法
可以近似理解为,useXXStore() 得到的就是一个 XXStore 的 Singleton。它是一个具有 reactivity 的对象,并且身上附加了一些 getter 和成员方法。
不过在实现层面,算是有一些遗憾。目前 pinia 的类型声明极度依赖于 defineStore 的类型体操(类似于 vue 的 defineComponent),而如此复杂的推导也是有代价的。如果你偶尔写了“半个函数”,导致 defineStore 类型推导无法收敛,那么整个 store 的类型都会挂掉,只会得到满屏幕的红线,并且鼠标移上去也无法获得任何有用的提示信息。
对 composition API 的支持更好
这样,调用 hasFeature 的地方就不需要再显式地 useSubscriptionStore(),使用者甚至可以认为 hasFeature 是一个 magic,而不用知道它背后的 SubscriptionStore 任何细节。
也就是说,一切我们在用 composition API 开发中常用的组合逻辑和 pattern,对于 pinia store 来说都适用。
虽然用 vuex 的 composition API 可以做到完全一样的事情,但是坦率而言,pinia 以 composition API 为出发点就让人很有这种冲动去做这样的重构 ……